from typing import Type, Union, Optional
from sqlalchemy.orm import Session
from schemas.users import UserRoleForm, UserForm
from datetime import datetime
from sqlalchemy import or_
from db import models


class Users:
    @staticmethod
    def get(db: Session, id: int) -> Union[None, models.Users]:
        return db.query(models.Users).filter_by(id=id, status=1).first()

    @staticmethod
    def gets(db: Session, page: int, per_page: int, kw: Optional[str] = None):
        if kw is None:
            count = db.query(models.Users).filter_by(status=1).count()
            data = db.query(models.Users).filter_by(status=1).offset(page * per_page).limit(per_page).all()
        else:
            count = db.query(models.Users).filter_by(status=1).filter(
                or_(
                    models.Users.nickname.like(f"%{kw}%"),
                    models.Users.username.like(f"%{kw}%")
                )
            ).count()
            data = db.query(models.Users).filter_by(status=1).filter(
                or_(
                    models.Users.nickname.like(f"%{kw}%"),
                    models.Users.username.like(f"%{kw}%")
                )
            ).offset(page * per_page).limit(per_page).all()
        return count, data

    @staticmethod
    def delete(db: Session, id: int) -> None:
        db.query(models.Users).filter_by(id=id).update({
            "status": 0,
        })
        db.commit()

    @staticmethod
    def create(db: Session, data: UserForm) -> models.Users:
        item = models.Users(
            nickname=data.nickname,
            username=data.username,
            password=data.password,
            avatar=data.avatar,
            permissions=data.permissions,
            roles=data.roles,
            created_on=data.created_on,
            created_by=data.created_by
        )
        db.add(item)
        db.commit()
        return item

    @staticmethod
    def count(db: Session, status: Union[None, int] = None) -> int:
        if status is None:
            return db.query(models.Users).count()
        else:
            return db.query(models.Users).filter_by(status=status).count()

    @staticmethod
    def get_by_email(db: Session, email: str) -> Union[None, models.Users]:
        return db.query(models.Users).filter_by(email=email, status=1).first()

    @staticmethod
    def get_by_username(db: Session, username: str) -> Union[None, models.Users]:
        return db.query(models.Users).filter_by(username=username, status=1).first()

    @staticmethod
    def get_by_name_or_email(db: Session, text: str) -> Union[None, models.Users]:
        return db.query(models.Users).where(or_(
            models.Users.username == text,
            models.Users.email == text)).filter_by(status=1).first()


class UserRole:
    @staticmethod
    def create(db: Session, data: UserRoleForm) -> models.UserRole:
        item = models.UserRole(
            id=data.id,
            name=data.name,
            permissions=data.permissions,
            created_on=datetime.now(),
            created_by=data.created_by
        )
        db.add(item)
        db.commit()
        return item

    @staticmethod
    def get(db: Session, id_: int) -> Union[models.UserRole, None]:
        return db.query(models.UserRole).get(id_)

    @staticmethod
    def update(db: Session, data: UserRoleForm) -> None:
        db.query(models.UserRole).filter_by(id=data.id).update({
            "name": data.name,
            "permissions": data.permissions,
            "updated_on": datetime.now(),
            "updated_by": data.updated_by
        })
        db.commit()

    @staticmethod
    def delete(db: Session, id: int) -> None:
        db.query(models.UserRole).filter_by(id=id).update({
            "status": 0,
        })
        db.commit()

    @staticmethod
    def gets(db: Session, page: int, per_page: int) -> tuple[int, list[Type[models.UserRole]]]:
        count = db.query(models.UserRole).filter_by(status=1).count()
        data = db.query(models.UserRole).offset(page * per_page).limit(per_page).all()
        return count, data
